package org.webdb.webapp.picocontainer;

import java.io.IOException;
import java.lang.reflect.Type;

import javax.management.RuntimeErrorException;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

import org.picocontainer.MutablePicoContainer;
import org.picocontainer.PicoCompositionException;
import org.picocontainer.PicoContainer;
import org.picocontainer.adapters.AbstractAdapter;
import org.picocontainer.web.PicoScopedContainers;
import org.picocontainer.web.PicoServletContainerFilter;

public class PicoRegistry extends PicoServletContainerFilter {

    public static class ServletContextInjector extends
            AbstractAdapter<ServletContext> {

        /**
         * 
         */
        private static final long serialVersionUID = 1L;
        private ServletContext servletContext;

        public ServletContextInjector(ServletContext servletContext) {
            super(ServletContext.class, ServletContext.class);
            this.servletContext = servletContext;
        }

        @Override
        public ServletContext getComponentInstance(PicoContainer container,
                Type into) throws PicoCompositionException {
            return servletContext;
        }

        @Override
        public String getDescriptor() {
            return ServletContext.class.getName();
        }

        @Override
        public void verify(PicoContainer container)
                throws PicoCompositionException {
            // TODO:
        }

    }

    protected static final ThreadLocal<MutablePicoContainer> appContainer = new ThreadLocal<MutablePicoContainer>();
    protected static final ThreadLocal<MutablePicoContainer> reqContainer = new ThreadLocal<MutablePicoContainer>();
    /**
     * 
     */
    private static final long serialVersionUID = 1L;
    protected static final ThreadLocal<MutablePicoContainer> sesContainer = new ThreadLocal<MutablePicoContainer>();

    public static PicoContainer getAppContainer() {
        return appContainer.get();
    }

    public static PicoContainer getRequestContainer() {
        return reqContainer.get();
    }

    public static PicoContainer getSessionContainer() {
        return sesContainer.get();
    }

    private PicoScopedContainers psc;
    private boolean stateless = false;

    @Override
    public void doFilter(ServletRequest req, ServletResponse resp,
            FilterChain filterChain) throws IOException, ServletException {
        try {
            super.doFilter(req, resp, filterChain);
        } catch (Throwable t) {

            reqContainer.get().stop();
            reqContainer.get().dispose();

            try {
                psc.getRequestStoring().invalidateCacheForThread();
                psc.getRequestState().invalidateStateModelForThread();
            } catch (Exception e) {
                // TODO: handle exception
            }

            if (!stateless) {
                try {
                    psc.getSessionStoring().invalidateCacheForThread();
                    psc.getSessionState().invalidateStateModelForThread();
                } catch (Exception e) {
                    // TODO: handle exception
                }
            }

            appContainer.remove();
            sesContainer.remove();
            reqContainer.remove();

            if (t instanceof Error)
                throw new RuntimeErrorException((Error) t);

            throw new RuntimeException(t);
        }
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

        super.init(filterConfig);
    }

    @Override
    protected void setAppContainer(MutablePicoContainer container) {
        setThreadLocal(appContainer, container);
    }

    @Override
    protected void setRequestContainer(MutablePicoContainer container) {
        setThreadLocal(reqContainer, container);
    }

    @Override
    protected void setSessionContainer(MutablePicoContainer container) {
        setThreadLocal(sesContainer, container);
    }

    private void setThreadLocal(ThreadLocal<MutablePicoContainer> threadLocal,
            MutablePicoContainer container) {
        if (container == null)
            threadLocal.remove();
        else
            threadLocal.set(container);
    }

}
